package com.lvhr.rpc.proxy;

import cn.hutool.core.collection.CollUtil;
import com.lvhr.rpc.*;
import com.lvhr.rpc.config.RpcConfig;
import com.lvhr.rpc.constant.RpcConstant;
import com.lvhr.rpc.fault.retry.RetryStrategy;
import com.lvhr.rpc.fault.retry.RetryStrategyFactory;
import com.lvhr.rpc.fault.tolerant.TolerantStrategy;
import com.lvhr.rpc.fault.tolerant.TolerantStrategyFactory;
import com.lvhr.rpc.loadbalancer.LoadBalancer;
import com.lvhr.rpc.loadbalancer.LoadBalancerFactory;
import com.lvhr.rpc.model.RpcRequest;
import com.lvhr.rpc.model.RpcResponse;
import com.lvhr.rpc.model.ServiceMetaInfo;
import com.lvhr.rpc.registry.Registry;
import com.lvhr.rpc.registry.RegistryFactory;
import com.lvhr.rpc.server.tcp.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务代理（JDK动态代理）
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {

    /**
     * 调用代理
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 特殊处理 Object 类的方法
        if (Object.class.equals(method.getDeclaringClass())) {
            return handleObjectMethod(proxy, method, args);
        }
        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        // 构造请求
        // fixme method.getDeclaringClass().getName()获取的是接口的完整路径
        //如果服务端和客户端定义的接口放在不同路径会导致无法调用
        //应将不含路径的接口名注册到LocalRegistry
        String serviceName = method.getDeclaringClass().getName();
        Map<String, Object> attachments = new ConcurrentHashMap<>();
        attachments.put("token",rpcConfig.getSecurityConfig().getToken());
        attachments.put("timestamp", System.currentTimeMillis());
        attachments.put("X-Nonce", UUID.randomUUID().toString()); // 随机数防重放
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .attachments(attachments)
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        if (rpcConfig.getSecurityConfig().isEnableSignature()){
            // 6. 动态签名（代理层集成）
            new ProxySigner(rpcConfig.getSecurityConfig().getSecretKey()).sign(rpcRequest);
        }
        // 从注册中心获取服务提供者请求地址
        Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
        ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
        serviceMetaInfo.setServiceName(serviceName);
        serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
        List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
        if (CollUtil.isEmpty(serviceMetaInfoList)) {
            throw new RuntimeException("暂无服务地址");
        }

        // 负载均衡
        LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
        // 将调用方法名（请求路径）作为负载均衡参数
        Map<String, Object> requestParams = new HashMap<>();
        requestParams.put("methodName", rpcRequest.getMethodName());
        log.info("methodName = {} ",rpcRequest.getMethodName());
        ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList);
        log.info("selectedServiceMetaInfo = {}",selectedServiceMetaInfo);

        // rpc 请求
        // 使用重试机制
        RpcResponse rpcResponse;
        try {
            RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategyConfig().getRetryStrategy());
            retryStrategy.init(rpcConfig);
            rpcResponse = retryStrategy.doRetry(() ->
                    VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo)
            );
        } catch (Exception e) {
            // 容错机制
            TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
            Map<String, Object> context = new HashMap<>();
            context.put("serviceMetaInfoList", serviceMetaInfoList); // 所有可用服务节点
            context.put("selectedServiceMetaInfo", selectedServiceMetaInfo); // 当前选择的节点
            context.put("rpcRequest", rpcRequest); // 原始请求
            context.put("exception", e);
            rpcResponse = tolerantStrategy.doTolerant(context, e);
            return rpcResponse.getData();


        }
        return rpcResponse.getData();
    }


//    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        // 特殊处理 Object 类的方法
//        if (Object.class.equals(method.getDeclaringClass())) {
//            return handleObjectMethod(proxy, method, args);
//        }
//        // 指定序列化器
//        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());
//
//        // 构造请求
//        // fixme method.getDeclaringClass().getName()获取的是接口的完整路径
//        //如果服务端和客户端定义的接口放在不同路径会导致无法调用
//        //应将不含路径的接口名注册到LocalRegistry
//        String serviceName = method.getDeclaringClass().getName();
//        RpcRequest rpcRequest = RpcRequest.builder()
//                .serviceName(serviceName)
//                .methodName(method.getName())
//                .parameterTypes(method.getParameterTypes())
//                .args(args)
//                .build();
//        try {
//            // 从注册中心获取服务提供者请求地址
//            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
//            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
//            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
//            serviceMetaInfo.setServiceName(serviceName);
//            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
//            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
//            if (CollUtil.isEmpty(serviceMetaInfoList)) {
//                throw new RuntimeException("暂无服务地址");
//            }
//            ServiceMetaInfo selectedServiceMetaInfo = serviceMetaInfoList.get(0);
//            // 发送 TCP 请求
//            RpcResponse rpcResponse = VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo);
//            return rpcResponse.getData();
//        } catch (Exception e) {
//            throw new RuntimeException("调用失败");
//        }
//    }


    /**
     * 处理 Object 类方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     */
    private Object handleObjectMethod(Object proxy, Method method, Object[] args) {
        switch (method.getName()) {
            case "toString":
                return "ServiceProxy@" + System.identityHashCode(proxy);  // 返回代理标识
            case "hashCode":
                return System.identityHashCode(proxy);
            case "equals":
                return proxy == args;  // 直接比较对象引用
            default:
                throw new UnsupportedOperationException("未实现的方法: " + method);
        }
    }


//    /**
//     * 调用代理
//     *version=2.0
//     * @return
//     * @throws Throwable
//     */
//    @Override
//    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        // 特殊处理 Object 类的方法
//        if (Object.class.equals(method.getDeclaringClass())) {
//            return handleObjectMethod(proxy, method, args);
//        }
//        // 指定序列化器
//        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());
//
//        // 构造请求
//        // fixme method.getDeclaringClass().getName()获取的是接口的完整路径
//        //如果服务端和客户端定义的接口放在不同路径会导致无法调用
//        //应将不含路径的接口名注册到LocalRegistry
//        String serviceName = method.getDeclaringClass().getName();
//        RpcRequest rpcRequest = RpcRequest.builder()
//                .serviceName(serviceName)
//                .methodName(method.getName())
//                .parameterTypes(method.getParameterTypes())
//                .args(args)
//                .build();
//        try {
//            // 序列化
//            byte[] bodyBytes = serializer.serialize(rpcRequest);
//
//
//            // 从注册中心获取服务提供者请求地址
//            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
//            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
//            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
//            serviceMetaInfo.setServiceName(serviceName);
//            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
//            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
//            if (CollUtil.isEmpty(serviceMetaInfoList)) {
//                throw new RuntimeException("暂无服务地址");
//            }
//            ServiceMetaInfo selectedServiceMetaInfo = serviceMetaInfoList.get(0);
//
//            // 发送请求
//            try (HttpResponse httpResponse = HttpRequest.post(selectedServiceMetaInfo.getServiceAddress())
//                    .body(bodyBytes)
//                    .execute()) {
//                byte[] result = httpResponse.bodyBytes();
//                // 反序列化
//                RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
//                return rpcResponse.getData();
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//
//        return null;
//    }

//    /**
//     * 调用代理
//     *version = 1.0
//     * @param proxy
//     * @param method
//     * @param args
//     * @return
//     * @throws Throwable
//     */
//    @Override
//    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        // 显式处理 Object 方法（如 toString、hashCode 等）
//        if (method.getDeclaringClass() == Object.class) {
//            return handleObjectMethod(proxy, method, args);
//        }
//        //指定序列化器
////        Serializer serializer = new JdkSerializer();
//        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());
//// 修改后：
//        if (method.getParameterCount() > 0 && args == null) {
//            // 仅校验有参数的方法（避免无参方法被误判）
//            throw new IllegalArgumentException("参数不可为空");
//        }
//
//        //构造请求
//        RpcRequest rpcRequest = RpcRequest.builder()
//                .serviceName(method.getDeclaringClass().getName())
//                .methodName(method.getName())
//                .parameterTypes(method.getParameterTypes())
//                .args(args)
//                .build();
//
//        try {
//            //序列化
//            byte[] bodyBytes = serializer.serialize(rpcRequest);
//            //发送请求
//            //todo注意，这里地址被编码了（需要注册中心和服务发现机制解决）
//            try (HttpResponse httpResponse = HttpRequest.post("http://localhost:8080")
//                    .body(bodyBytes)
//                    .execute()) {
//                byte[] res = httpResponse.bodyBytes();
//                //反序列化
//                RpcResponse rpcResponse = serializer.deserialize(res, RpcResponse.class);
//                return rpcResponse.getData();
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }

}
